package com.czp.opensource.profiler.generate;

import java.util.List;

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.CtNewMethod;

import com.czp.opensource.profiler.ClassAgent;
import com.czp.opensource.profiler.log.LogWrapper;

/**
 * 
 * @description
 *       <p>默认生成时间统计代理代码,采用javassist</p>
 * @author 
 *        Czp
 * @version 
 *        1.0(2014-5-10)
 *
 */
public class DefaultCodeGenerate implements CodeGenerator{

	public final static String beforCall ="long startTime = System.currentTimeMillis();\n";  
    public final static String afterCall ="long endTime = System.currentTimeMillis();\n";
    public final static String logMethod = LogWrapper.class.getName()+".getInstance().info(\"Method[";
    public final static  String constStr = "]-cost:\" +(endTime - startTime) +\"ms\");\n";
	
    /**
	 * 
	 * @desc:
	 *     <p>创建方法的代理</p> 
	 *     <p>
	 *         long startTime = System.currentTimeMillis();
	 *         callOrginalMethod(xx);
	 *         long endTime = System.currentTimeMillis();
	 *         String cost = "method-cost:"+(endTime - startTime);
	 *         LogingThread.getInstance().sendLog();
	 *     </p>
	 * @param methodName
	 *        被代理的方法的名称
	 * @param newMethodName
	 *        被代理方法的新名称:methodName$Proxy 
     * @param isVoid 
     *        是否返回void   
	 * @return
	 */
	private String generate(String className, String methodName,
			String newMethodName, boolean isVoid) {
		StringBuilder methodBody =new StringBuilder();  
		 methodBody.append("{\n");  
		 methodBody.append(beforCall); 
		 if(isVoid)
		 {
			 methodBody.append(newMethodName);
			 methodBody.append("($$);\n");   
		 }else{
			 methodBody.append("Object result=");
			 methodBody.append(newMethodName);
			 methodBody.append("($$);\n"); 
		 }	 
		 methodBody.append(afterCall);  
		 methodBody.append(logMethod);
		 methodBody.append(className);
		 methodBody.append(".");
		 methodBody.append(methodName);
		 methodBody.append(constStr);  
		 if(!isVoid)
		 {
			 methodBody.append("return ($r)result;\n"); 
		 }
		 methodBody.append("}");
		return methodBody.toString();
	}

    /**
	 * 
	 * @desc:
	 *     <p>注入拦截代码</p>
	 *     <p>setup1:获取原来的方法,并修改方法名为:method$Proxy</p> 
	 *     <p>setup2:复制原来方法的字节码CtNewMethod.copy</p>
	 *     <p>setup3:构造新的方法并调用原来的方法method$Proxy($$),$$:取所有参数</p>
	 *     <p>setup4:返回新的字节码</p>
	 * @param className
	 * @param classfileBuffer
	 * @return
	 */
	@Override
	public byte[] generate(String className,ClassAgent agent, byte[] classfileBuffer) {
		
		LogWrapper log = LogWrapper.getInstance();
		
		try {
		
			List<String> filterMethods = agent.getTargetMethods();
			ClassPool classPool = new ClassPool(true);
			CtClass ctClass = classPool.get(className);
			CtMethod[] methods =  ctClass.getMethods();
			
			for (String methodName : filterMethods) {
				 CtMethod ctMethod = getMethod(methods,methodName);
				 if(ctMethod==null)
					 continue;
				//change methodName because it can like test* 
				 methodName = ctMethod.getName();
				 CtClass returnType = ctMethod.getReturnType();
				 boolean isVoid = returnType.equals(CtClass.voidType);
				 String newMethodName = methodName+"$Proxy";
				 ctMethod.setName(newMethodName);
				 CtMethod newMethod = CtNewMethod.copy(ctMethod, methodName, ctClass,null);
				 String code = generate(className, methodName, newMethodName,isVoid);
				 newMethod.setBody(code);  
                 ctClass.addMethod(newMethod); 
				 log.debug(code);
			}
			
			return ctClass.toBytecode();  
		} catch (Exception e) {
			log.info(e.toString());
		}
		return null;
	}

	/**
	 * 
	 * @desc:
	 *     <p>找到匹配的方法</p> 
	 * @param methods
	 * @param methodName
	 * @return
	 */
	private CtMethod getMethod(CtMethod[] methods, String methodName) {
		
		if(methodName.endsWith("*"))
			methodName = methodName.substring(0, methodName.length()-1);
		
		for (CtMethod ctMethod : methods) {
		   if(ctMethod.getName().startsWith(methodName))
			   return ctMethod;
		}
		return null;
	}
}
